#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <asm/atomic.h>

#include <linux/device.h>

#include "comm.h"


/*	##############	原子变量	###############
	int a;		    atomic_t cnt;
	a = 5;			atomic_set(&cnt,5);   void atomic_set(atomic_t *cnt,int i)

	a = a+5;		atomic_add(5,&cnt);   void atomic_add(int i,atomic_t *v);
								int atomic_add_return(int i,atomic_t *v)
	a++;			atomic_inc(&cnt);	  void atomic_inc(atomic_t *v);
								int atomic_inc_return(atomic_t *v);
	a = a-5;		atomic_sub(5,&cnt);	  void atomic_sub(int i,atomic_t *v);
								int  atomic_sub_return(int i,atomic_t *v);
	a--;			atomic_dec(&cnt);	  void atomic_dec(atomic_t *v);
								void atomic_dec_return(atomic_t *v);
								
	int b = a;		int b = atomic_read(&cnt); int atomic_read(atomic_t *v);

	我们用例,  文件只能被打开一次
	open的是 +1,如果 结果 >1 则open返回错误
	当close的时候,别忘了 -1


	#############	互斥锁	############
	1.定义并初始化
	struct mutex mlock;
	void mutex_init(struct mutex *lock);

	2.访问共享资源 之前, 加锁
	void mutex_lock(struct mutex *lock)

	3.访问玩之后解锁
	void mutex_unlock(struct mutex * lock)



	##########	自旋锁	#################
	1.定义并初始化
	spinlock_t slock;
	void spin_lock_init (spinlock_t *lock)

	2.访问共享资源 之前, 加锁
	void spin_lock(spinlock_t *lock)
	
	3.访问玩之后解锁
	spin_unlock(spinlock_t *lock)

*/


#define	   GPX2CONF		(0x11000c40)
#define	   GPX2DATA		(0x11000c44)


struct global_struct {
	int major,minor;

	void *gpx2conf ;
	void *gpx2data;
	struct cdev  cdevobj; 		/*字符设备对象*/

	struct class *cls;
	struct device * device;

	atomic_t opencnt; 		/*原子变量*/
	struct mutex mlock;		/*互斥锁	*/
	spinlock_t slock;
};




struct global_struct  gstruct;

void led_init(void)
{

	int val;
	
	gstruct.gpx2conf = ioremap(GPX2CONF,4);
	gstruct.gpx2data = ioremap(GPX2DATA,4);

	/*配置 gpx2 7 管脚为输出模式*/
	val = readl(gstruct.gpx2conf);
	val = val & ( ~( 0xf<<28 ) );
	val = val |  (1<<28);
	writel(val,gstruct.gpx2conf);

	/*关灯*/
	val = readl(gstruct.gpx2data);
	val = val &  ( ~(1<<7) );
	writel(val,gstruct.gpx2data);

	return  ;
}

void led_ctrl(int on)
{
	int val;

	if(on == 0){
		/*关灯*/
		val = readl(gstruct.gpx2data);
		val = val &  ( ~(1<<7) );
		writel(val,gstruct.gpx2data);
	}else {
		/*关灯*/
		val = readl(gstruct.gpx2data);
		val = val | (1<<7);
		writel(val,gstruct.gpx2data);
	}


}



int chdev_open(struct inode *inode, struct file *file)
{
	int  cnt;
	struct global_struct *pt_gstruct;

	pt_gstruct = container_of(inode->i_cdev, struct global_struct ,cdevobj);    

	printk("%s->%d\n",__func__,__LINE__);


	/* 我们在open的时候, 往里面 保存一个 地址*/
	file->private_data = pt_gstruct;


	
	cnt = atomic_inc_return(&pt_gstruct->opencnt);
	if(cnt >5 ){
		printk("%s->%d only support open once\n",__func__,__LINE__);
		atomic_dec(&pt_gstruct->opencnt);
		return -EBUSY;
	}



	led_init();		/*led init*/


	return 0;		/*0告诉内核,该函数执行成功    失败 负值*/
}

int chdev_release(struct inode *inode, struct file *file)
{
	struct global_struct *pt_gstruct = file->private_data;

	atomic_dec(&pt_gstruct->opencnt);
	
	printk("%s->%d  major%d minor%d\n",__func__,__LINE__,
		pt_gstruct->major,pt_gstruct->minor);

	return 0;		/*0告诉内核,该函数执行成功*/
}


/*
int      read(int          fd,   char *buf,        int size)
ssize_t  read(struct file *file, char __user *usr, size_t size, loff_t *loff)

*/
ssize_t chdev_read(struct file *file, char __user *usr, size_t size, loff_t *loff)
{
	int ret;
	struct msg msgkernel;
	struct global_struct *pt_gstruct = file->private_data;

	printk("%s->%d  major%d minor%d\n",__func__,__LINE__,
		pt_gstruct->major,pt_gstruct->minor);



	memset(&msgkernel,0,sizeof(msgkernel));
	msgkernel.temp = 33;
	msgkernel.humidity = 77;
	msgkernel.windspeed = 11;
	strcpy(msgkernel.des,"hello msg come from kernel space");
	
	/*返回值,  是实际未拷贝的字节数据*/
	ret = copy_to_user(usr,&msgkernel,size);
	if(ret ){
		printk("%s->%d copy_to_user err\n",__func__,__LINE__);
		return size - ret;
	}


	return size;		/*返回实际拷贝的大小*/
}



ssize_t chdev_write(struct file *file, const char __user *usr, size_t size, loff_t *loff)
{
	int ret;
	struct msg msgkernel;
	struct global_struct *pt_gstruct = file->private_data;

	printk("%s->%d  major%d minor%d\n",__func__,__LINE__,
		pt_gstruct->major,pt_gstruct->minor);

//	spin_lock(&pt_gstruct->slock);

	/*注意, 进程 在没有获得锁的情况下,可能睡眠*/
	mutex_lock(&pt_gstruct->mlock);
	printk("%s->%d   111111111\n",__func__,__LINE__);
	msleep(1*1000);
	printk("%s->%d   222222222\n",__func__,__LINE__);
	msleep(1*1000);
	printk("%s->%d   333333333\n",__func__,__LINE__);
	msleep(1*1000);
	printk("%s->%d   444444444\n",__func__,__LINE__);
	msleep(1*1000);
	printk("%s->%d   555555555\n",__func__,__LINE__);
	mutex_unlock(&pt_gstruct->mlock);
//	spin_unlock(&pt_gstruct->slock);

	memset(&msgkernel,0,sizeof(msgkernel));
	/*返回值,  是实际未拷贝的字节数据*/
	ret = copy_from_user(&msgkernel,usr,sizeof(msgkernel));
	if(ret ){
		printk("%s->%d copy_from_user err\n",__func__,__LINE__);
		return size - ret;
	}

	printk("%s->%d kernel get msg temp%d humidity%d windspeed%d des:%s\n",
		__func__,__LINE__,msgkernel.temp,msgkernel.humidity,msgkernel.windspeed,msgkernel.des);


	led_ctrl(msgkernel.on);

	return size;		/*返回实际拷贝的大小*/
}


/*
int  ioctl(               int fd,    int         cmd ,        long  args);
long chdev_ioctl(struct file *file, unsigned int cmd, unsigned long args)
*/
long chdev_ioctl(struct file *file, unsigned int cmd, unsigned long args)
{
	int ret;
	
	struct global_struct *pt_gstruct = file->private_data;

	struct tv_switch tv_sw;
	struct tv_chnl   tv_chnl;
	struct tv_set    tv_set;
	struct tv_vol    tv_vol;
	struct tv_stat   tv_stat;

	tv_set = tv_set;
	tv_vol = tv_vol;

	printk("%s->%d  major%d minor%d\n",__func__,__LINE__,
		pt_gstruct->major,pt_gstruct->minor);


	switch(cmd){
	case TV_CTRL_SWITCH:
		ret = copy_from_user(&tv_sw,(void *)args,sizeof(tv_sw));

		printk("%s->%d TV_CTRL_SWITCH get on%d\n",__func__,__LINE__,tv_sw.on);
		
		break;
	case TV_CTRL_CHNL:
		ret = copy_from_user(&tv_chnl,(void *)args,sizeof(tv_chnl));

		printk("%s->%d TV_CTRL_CHNL get updown%d chnl%d\n",
				__func__,__LINE__,tv_chnl.updown,tv_chnl.chnl_no);
		break;

	case TV_CTRL_SET:

		break;

	case TV_CTRL_VOL:

		break;

	case TV_CTRL_STAT:
		tv_stat.chnl_no = 123;
		tv_stat.light = 135;
		tv_stat.vol = 31;
		ret = copy_to_user((void *)args,&tv_stat,sizeof(tv_stat));
		
		break;
	default:
		printk("%s->%d unkown cmd%d\n",__func__,__LINE__,cmd);
		return -EINVAL;
	}

	return 0;
}


struct file_operations  fops = {
	.open = chdev_open,
	.release = chdev_release,
	.read = chdev_read,
	.write = chdev_write,
	.unlocked_ioctl = chdev_ioctl,
};



int  md_init (void )
{
	dev_t devno;
	int ret;


	/*原子变量用法 第一步 定义并初始化*/
	atomic_set(&gstruct.opencnt,0);

	/*初始化 互斥锁*/
	mutex_init(&gstruct.mlock);

	/*初始化自旋锁*/
	spin_lock_init (&gstruct.slock);

	
	/*如何实现字符设备驱动
	第一步 动态申请设备号		注意,申请的主设备号 是一致的

	int  alloc_chrdev_region(dev_t * dev,unsigned baseminor,
						unsigned count,const char * name);
		
		dev: 本质上就是u32 保存了   内核给你的   第一个设备号
		baseminor:次设备号 起始数值   你可以指定  0- (2^20-1)
				一般 从0就可以了
		count:表示这一次你要申请多少个 连续的设备号
		name, 自己启名字


	第二步
		定义并初始化 字符设备对象 [属性 方法]
		struct cdev {
			const struct file_operations *ops;	方法集合
			dev_t dev;							设备号
		};

		
		void cdev_init(struct cdev * cdev,const struct file_operations * fops);
		fops,该对象的方法集合
		
		int cdev_add(struct cdev   p[],dev_t dev,unsigned count);
		dev:该字符设备设备对应的 设备号
		count,表示 你这一次需要添加多少个 字符设备驱动 对象

		返回值,  错误<0   

		第三步,  在用户层创建一个字符设备文件
			1)创建一个内核的类
			struct class * class_create(owner,name);
				owner: THIS_MODULE
				name:  你给类起的名字
			2)创建节点, 在用户层/dev/目录下  创建一个 字符设备文件/节点
			struct device *device_create(struct class *class, 
					struct device *parent,
			    		 dev_t devt, void *drvdata, const char *fmt, ...)
			   class:前面创建的class
			   parent: 没有使用到,为null
			   devt: 前面申请的设备号,要和 字符设备驱动对象的 保持一致
			   drvdata: 节点的私有数据,为null
			   printf(const char *fmt, ...)  printf("chdev%s %d %c %x",xxxx,xx)
	*/

	

	/*第一步 向内核 申请设备号*/
	ret = alloc_chrdev_region(&devno,0,1,"chdev test devno");
	if(ret <0 ){
		printk("%s->%d alloc_chrdev_region err%d\n",__func__,__LINE__,ret);
		return -12;
	}

	gstruct.major = MAJOR(devno );
	gstruct.minor = MINOR(devno );
	devno=  MKDEV(gstruct.major,gstruct.minor);
	printk("%s->%d get major%d  minor%d devno%d\n",
			__func__,__LINE__,gstruct.major,gstruct.minor,devno);


	/*第二步  创建一个字符设备对象*/
	cdev_init(&gstruct.cdevobj,&fops);
	ret = cdev_add(&gstruct.cdevobj,devno,1);
	if(ret <0){
		printk("%s->%d cdev_add err%d\n",__func__,__LINE__,ret);
		return -13;
	}


	/*字符设备第三步
		在用户层 创建字符设备节点
	*/

	gstruct.cls = class_create(THIS_MODULE,"chdev class");
	if(!gstruct.cls){
		printk("%s->%d class_create err \n",__func__,__LINE__ );
		return -14;
	}

	/*会在 /dev目录下 创建  /dev/chdev0 的设备节点/文件    */
	gstruct.device = device_create(gstruct.cls,NULL,devno,NULL,"chdev%d",0);
	if(!gstruct.device){
		printk("%s->%d device_create err \n",__func__,__LINE__ );
		return -15;
	}

	printk("%s->%d\n",__func__,__LINE__);
	return 0;
}


/*注意,释放的时候 要和 init 逆序*/
void  md_exit (void )
{

	device_destroy(gstruct.cls, MKDEV(gstruct.major,gstruct.minor));		/*释放 文件节点*/
	class_destroy(gstruct.cls);

	cdev_del(&gstruct.cdevobj);	/*释放 字符设备驱动对象*/

	unregister_chrdev_region( MKDEV(gstruct.major,gstruct.minor),1);	/*释放设备号*/
	printk("%s->%d\n",__func__,__LINE__);
}


/*模块代码如何编写

	1.入口    module_init是内核的一个宏
	module_init(    int (*init)(void )   );


	2.出口
	module_exit(   void (*exit)(void )    );

*/

module_init(md_init);
module_exit(md_exit);

MODULE_LICENSE("GPL");



